package com.yzpass.api.common.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.yzpass.api.user.entity.User;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.*;

public class JwtUtil {
    private static final String RSA = "RSA";

    public record UserInfo(UUID userId){}
    public static Optional<UserInfo> verify(String token, String publicKey) {
        try {
            RSAPublicKey rsaPublicKey = (RSAPublicKey) RSAUtil.getPublicKey(publicKey);
            Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);


            UUID userId = UUID.fromString(jwt.getSubject());
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            if (requestAttributes != null) {
                requestAttributes.setAttribute("userId", userId, RequestAttributes.SCOPE_REQUEST);
            }
            return Optional.of(new UserInfo(userId));
        } catch (Exception exception) {
            return Optional.empty();
        }
    }

    public static String generate(Date expTime, String privateKey, User user) {
        try {
            var rsaPrivateKey = (RSAPrivateKey) RSAUtil.getPrivateKey(privateKey);
            Map<String, Object> mapHeader = new TreeMap<>();
            mapHeader.put("alg", RSA);
            Algorithm algorithm = Algorithm.RSA256(null, rsaPrivateKey);

            return JWT.create()
                    .withHeader(mapHeader)
                    .withIssuedAt(new Date())
                    .withExpiresAt(expTime)
                    .withSubject(user.getUserId().toString())
                    .sign(algorithm);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
